var GAME = require('./game');

var NOTHING = 0;
var BOMB = 1;
var REPAIR = 2;
var BULLET = 3;
var SHELL = 4;
var HIDE = 5;
var RADAR = 6;
var POWERUP = 7;
var SHOOT_ONE = 8;
var SHOOT_ALL = 9;
var SHOOT_LINE = 10;
var SHOOT_BOX = 11;
var FREEZE = 12;
var ONFIRE = 13;
var SPEEDUP = 14;
var CLEAR = 15;
exports.BOMB = BOMB;

var Z_HURT = 0;
var Z_REPAIR = 1;
var Z_PICK = 2;
var Z_SHOOT = 3;
exports.z_max = 4;

var FIELD_TYPE = 0;
var FIELD_Z = 1;
var FIELD_ATB = 2;
var FIELD_FAC = 3;

var SHOOTBOX_HITSTEP = 10;
var BOMB_RATE = 0.2;
var EMPTY_RATE = 0.5;

var g_mb = [];
function init() {
    g_mb = [
        [NOTHING, Z_PICK, {}, function () { return {_type:0}; }],
        [BOMB, Z_HURT, { hit: 30 }, function (data) { return new CBomb(data); }],
        [REPAIR, Z_REPAIR, { hp: 30 }, function (data) { return new CRepair(data); }],
        [BULLET, Z_PICK, { hit: 20, cnt: 4, update: true }, function (data) { return new CBullet(data); }],
        [SHELL, Z_PICK, { cnt: 2 }, function (data) { return new CShell(data); }],
        [HIDE, Z_PICK, { cnt: 3 }, function (data) { return new CHide(data); }],
        [RADAR, Z_PICK, { cnt: 3 }, function (data) { return new CRadar(data); }],
        [ONFIRE, Z_SHOOT, { hit: 10, cnt: 4 , update:true}, function (data) { return new COnFire(data); }],
        [SHOOT_ONE, Z_SHOOT, { hit: 30 }, function (data) { return new CShootOne(data); }],
        [SHOOT_ALL, Z_SHOOT, { hit: 30 }, function (data) { return new CShootAll(data); }],
        [SHOOT_LINE, Z_SHOOT, { hit: 30 }, function (data) { return new CShootLine(data); }],
        [SHOOT_BOX, Z_SHOOT, { hit: 40 }, function (data) { return new CShootBox(data); }],
        [FREEZE, Z_SHOOT, { cnt: 1 }, function (data) { return new CFreeze(data); }],
        [SPEEDUP, Z_PICK, { cnt: 4, up: 3 }, function (data) { return new CSpeedUp(data); }],
        [CLEAR, Z_REPAIR, {}, function (data) { return new CClear(data); }],
        [POWERUP, Z_PICK, {cnt:3, up:2}, function(data){return new CPowerUp(data);}]
    ];
}

exports.create = function (i) {
    var data = g_mb[BOMB];
	var rate = Math.random();
    if (rate > BOMB_RATE) {
		if(rate < EMPTY_RATE){
			data = g_mb[NOTHING];
		}
		else{
			var r = Math.floor(Math.random() * g_mb.length);
			data = g_mb[r];
		}
    }
    if (i) {
        g_mb.forEach(function (m) {
            if (i == m[0]) {
                data = m;
            }
        });
    }
    return data[FIELD_FAC](data);
};

function CBomb(data) {
    setBase.call(this, data);
	
	this.enter = function(player, room){
		player._hp -= this._hit;
		room._renderTime += 1000;
		room.onPlayerHit({}, player, 0);
		room.sendToRoom('ghit', {a:player._id, t:this._type, hp:player._hp});
	}
}

function CRepair(data){
    setBase.call(this, data);
	this._hp = data[FIELD_ATB].hp;
	
	this.enter = function(player, room){
	    if(player._hp>0){
	        player._hp += this._hp;
	        if (player._hp > player._maxhp) {
	            player._hp = player._maxhp;
	        }
	        room._renderTime += 500;
	        room.sendToRoom('ghit', { a: player._id, t: this._type, hp: player._hp });
	    }
	}
}

function CBullet(data){
    setBase.call(this, data);
	
	this.enter = function(player, room){
		addBuff.call(this, player, room);
	}
	
	this.update = function (player, room) {
		var near = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]];
		near.forEach(function(step){
			var pos = player._pos;
			var y = pos.y + step[GAME.TAG_Y];
			var my = GAME.clamp(y, 0, GAME.ROOM_HEIGHT-1);
			var x = pos.x + step[GAME.TAG_X];
			var mx = GAME.clamp(x, 0, GAME.ROOM_WIDTH-1);
			if(y==my && x==mx && this._cnt>0){
			    var grid = room._grids[y][x];
			    var hitter = grid && grid._owner && grid._owner[0];
				if(hitter && hitter._hp>0){
					this._cnt -= 1;
					room._renderTime += 500;
					room.onPlayerHit(player, hitter, this._hit);
					room.sendToRoom('ghit', { a: player._id, b: hitter._id, t: this._type, hp: hitter._hp });
				}
			}
		}, this);

		checkBuffOver.call(this, player, room);
	}
}

function CShell(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        addBuff.call(this, player, room);
        player._shell = 1;
    }

    this.update = function (player, room) {
        this._cnt -= 1;
        checkBuffOver.call(this, player, room);
    }

    this.leave = function (player, room) {
        player._shell = 0;
    }
}

function CHide(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        addBuff.call(this, player, room);
        player._hide = 1;
    }

    this.update = function (player, room) {
        this._cnt -= 1;
        checkBuffOver.call(this, player, room);
    }

    this.onhit = function (player, room) {
        this._cnt = 0;
        this.update(player, room);
    }

    this.leave = function (player, room) {
        player._hide = 0;
    }
}

function CRadar(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        addBuff.call(this, player, room);
        player._radar = 1;
    }

    this.update = function (player, room) {
        this._cnt -= 1;
        checkBuffOver.call(this, player, room);
    }

    this.leave = function (player, room) {
        player._radar = 0;
    }
}

function COnFire(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        var target = selectTarget(room, player);
        if (target) {
            room._renderTime += 1000;
            addBuff.call(this, target, room, player);
        }
    }

    this.update = function (player, room) {
        this._cnt -= 1;
        player._hp -= this._hit;
        room.onPlayerHit({}, player, 0);
		room.sendToRoom('ghit', { a: player._id, t: this._type, hp: player._hp });
        checkBuffOver.call(this, player, room);
    }
}

function CShootOne(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        var target = selectTarget(room, player);
        if (target) {
            room._renderTime += 1000;
            room.onPlayerHit(player, target, this._hit);
            room.sendToRoom('ghit', { a: player._id, b: target._id, t: this._type, hp: target._hp });
        }
    }
}

function CShootAll(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        room._players.forEach(function (target) {
            if (isTarget(target, player)) {
                room._renderTime += 500;
                room.onPlayerHit(player, target, this._hit);
                room.sendToRoom('ghit', { a: player._id, b: target._id, t: this._type, hp: target._hp });
            }
        }, this);
    }
}

function CShootLine(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        room._renderTime += 3000;
        var y = player._pos.y;
		var x = player._pos.x;
		
		room.sendToRoom('shootln', {a:player._id, x:x+1,y:y});
        for (var i = x; i < GAME.ROOM_WIDTH; ++i) {
            this.shoot(player, room, y, i);
        }
		room.sendToRoom('shootln', {a:player._id, x:x-1,y:y});
        for (var i = x; i >=0; --i) {
            this.shoot(player, room, y, i);
        }
		room.sendToRoom('shootln', {a:player._id, x:x,y:y+1});		
        for (var i = y; i < GAME.ROOM_HEIGHT; ++i) {
            this.shoot(player, room, i, x);
        }
		room.sendToRoom('shootln', {a:player._id, x:x,y:y-1});
        for (var i = y; i>=0; --i) {
            this.shoot(player, room, i, x);
        }		
    }

    this.shoot = function (player, room, y, x) {
        var grid = room._grids[y][x];
        var owner = grid && grid._owner
        if (!owner){
            return;
        }

        var target = grid._owner[0];
        if (isTarget(target, player, true)) {
            room.onPlayerHit(player, target, this._hit);
            room.sendToRoom('slhit', { a: player._id, b: target._id, t: this._type, hp: target._hp });
        }
    }
}

function CShootBox(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        room._renderTime += 1000;
		room.sendToRoom('shootbox', {a:player._id});
        room._players.forEach(function (target) {
            if (isTarget(target, player, true)) {
                var x = Math.abs(player._pos.x - target._pos.x);
                var y = Math.abs(player._pos.y - target._pos.y);
                var dist = Math.floor(Math.sqrt(x * x + y * y));
                var hit = this._hit - (dist - 1) * SHOOTBOX_HITSTEP;
                if (hit > 0) {
                    room.onPlayerHit(player, target, hit);
                    room.sendToRoom('sbhit', { a: player._id, b: target._id, t: this._type, hp: target._hp });
                }
            }
        }, this);
    }
}

function CFreeze(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        var target = selectTarget(room, player);
        if (target) {
            addBuff.call(this, target, room, player);
            target._freeze = true;
        }
    }

    this.update = function (player, room) {
        this._cnt -= 1;
        checkBuffOver.call(this, player, room);
    }

    this.onhit = function (player, room) {
        this._cnt = 0;
        this.update(player, room);
    }

    this.leave = function (player, room) {
        player._freeze = false;
    }
}

function CSpeedUp(data) {
    setBase.call(this, data);
    this._up = data[FIELD_ATB].up;

    this.enter = function (player, room) {
        if (addBuff.call(this, player, room)) {
            player._speed += this._up;
        }
    }

    this.update = function (player, room) {
        this._cnt -= 1;
        checkBuffOver.call(this, player, room);
    }

    this.leave = function (player, room) {
        player._speed -= this._up;
    }
}

function CClear(data) {
    setBase.call(this, data);

    this.enter = function (player, room) {
        room._renderTime += 1000;
        room.sendToRoom('clear', { a: player._id });

        room._players.forEach(function (target) {
            if (target != player) {
                for (t in target._dbuff) {
                    var buff = target._dbuff[t];
                    buff._cnt = 0;
                    checkBuffOver.call(buff, target, room);
                }
                target._dbuff = {}
            }
        }, this);
    }
}

function CPowerUp(data) {
    setBase.call(this, data);
    this._up = data[FIELD_ATB].up;

    this.enter = function (player, room) {
        addBuff.call(this, player, room);
        player._power = this._up;
    }

    this.update = function (player, room) {
        this._cnt -= 1;
        checkBuffOver.call(this, player, room);
    }

    this.leave = function (player, room) {
        this._power = 1;
    }
}

function isTarget(target, shooter, see) {
    return target && target != shooter && (see || shooter._radar >= target._hide) && target._hp > 0;
}

function selectTarget(room, shooter) {
    var idx = Math.floor(Math.random() * room._players.length);
    var num = room._players.length;
    while (num > 0) {
        var target = room._players[idx];
        if (isTarget(target, shooter)) {
            return target;
        }

        num -= 1;
        idx = (idx + 1) % room._players.length;
    }
    return null;
}

function setBase(data) {
    this._type = data[FIELD_TYPE];
    this._z = data[FIELD_Z];
    if (typeof data[FIELD_ATB].cnt == 'number') {
        this._cnt = data[FIELD_ATB].cnt;
    }
    if (typeof data[FIELD_ATB].hit == 'number') {
        this._hit = data[FIELD_ATB].hit;
    }
    if (typeof data[FIELD_ATB].update != 'undefined') {
        this._updatenow = true;
    }
}

function addBuff(player, room, shooter) {
    var newadd = false;
    var dbuffs = player._dbuff;
    if (typeof dbuffs[this._type] != 'undefined') {
        dbuffs[this._type]._cnt += this._cnt;
    }
    else {
        dbuffs[this._type] = this;
        newadd = true;
    }
    
    if (shooter) {
        room._renderTime += 1000;
        room.sendToRoom('gbuff', { a: shooter._id, b: player._id, t: this._type, cnt: dbuffs[this._type]._cnt });
    }
    else {
        room._renderTime += 500;
        room.sendToRoom('gbuff', { a: player._id, t: this._type, cnt: dbuffs[this._type]._cnt });
    }

    if (newadd && this._updatenow) {
        dbuffs[this._type].update(player, room);
    }
    return newadd;
}

function checkBuffOver(player, room) {
    if (this._cnt <= 0) {
        if (this.leave) {
            this.leave(player, room);
        }
        delete player._dbuff[this._type];
        room.sendToRoom('gbuff', { a: player._id, t: this._type, cnt: 0 });
        return true;
    }
    return false;
}

init();